/*
 * trace_test.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/time.h>
#include <stdbool.h>


#include "../slab/mm.h"

#define MAXLINE     1024 /* max string size */

/* Characterizes a single trace operation (allocator request) */
typedef struct {
    enum {ALLOC, FREE, REALLOC, HEAPCHECK} type; /* type of request */
    int index;                                   /* index for free() to use later */
    int size;                                    /* byte size of alloc/realloc request */
} traceop_t;

/* Holds the information for one trace file*/
typedef struct {
    int sugg_heapsize;   /* suggested heap size (unused) */
    int num_ids;         /* number of alloc/realloc ids */
    int num_ops;         /* number of distinct requests */
    int weight;          /* weight for this trace (unused) */
    traceop_t *ops;      /* array of requests */
    void** blocks;       /* array of ptrs returned by malloc/realloc... */
    size_t *block_sizes; /* ... and a corresponding array of payload sizes */
} trace_t;

/*
 * unix_error - Report a Unix-style error
 */
void unix_error(char *msg)
{
    printf("%s\n", msg);
    exit(1);
}

/*
 * read_trace - read a trace file and store it in memory
 */
trace_t *read_trace(char *tracedir, char *filename)
{
    FILE *tracefile;
    trace_t *trace;
    char type[MAXLINE];
    char path[MAXLINE];
    unsigned index, size;
    unsigned max_index = 0;
    unsigned op_index;

    /* Allocate the trace record */
    if ((trace = (trace_t *) malloc(sizeof(trace_t))) == NULL)
        unix_error("malloc 1 failed in read_trance");

    /* Read the trace file header */
    strcpy(path, tracedir);
    strcat(path, filename);
    char msg[256];
    int scaned_num = 0;
    if ((tracefile = fopen(path, "r")) == NULL) {
    sprintf(msg, "Could not open %s in read_trace", path);
    unix_error(msg);
    }
    scaned_num = fscanf(tracefile, "%d", &(trace->sugg_heapsize)); /* not used */
    scaned_num = fscanf(tracefile, "%d", &(trace->num_ids));
    scaned_num = fscanf(tracefile, "%d", &(trace->num_ops));
    scaned_num = fscanf(tracefile, "%d", &(trace->weight));        /* not used */

    /* We'll store each request line in the trace in this array */
    if ((trace->ops =
     (traceop_t *)malloc(trace->num_ops * sizeof(traceop_t))) == NULL)
    unix_error("malloc 2 failed in read_trace");

    /* We'll keep an array of pointers to the allocated blocks here... */
    if ((trace->blocks =
     (void**)malloc(trace->num_ids * sizeof(void*))) == NULL)
    unix_error("malloc 3 failed in read_trace");

    /* ... along with the corresponding byte sizes of each block */
    if ((trace->block_sizes =
     (size_t *)malloc(trace->num_ids * sizeof(size_t))) == NULL)
    unix_error("malloc 4 failed in read_trace");

    /* read every request line in the trace file */
    index = 0;
    op_index = 0;
    while (fscanf(tracefile, "%s", type) != EOF) {
    switch(type[0]) {
    case 'a':
        scaned_num = fscanf(tracefile, "%u %u", &index, &size);
        trace->ops[op_index].type = ALLOC;
        trace->ops[op_index].index = index;
        trace->ops[op_index].size = size;
        max_index = (index > max_index) ? index : max_index;
        break;
    case 'r':
        scaned_num = fscanf(tracefile, "%u %u", &index, &size);
        trace->ops[op_index].type = REALLOC;
        trace->ops[op_index].index = index;
        trace->ops[op_index].size = size;
        max_index = (index > max_index) ? index : max_index;
        break;
    case 'f':
        scaned_num = fscanf(tracefile, "%ud", &index);
        trace->ops[op_index].type = FREE;
        trace->ops[op_index].index = index;
        break;
    case 'c':
            trace->ops[op_index].type = HEAPCHECK;
        break;
    default:
        printf("Bogus type character (%c) in tracefile %s\n",
           type[0], path);
        exit(1);
    }
    op_index++;

    }
    fclose(tracefile);
    assert(max_index == trace->num_ids - 1);
    assert(trace->num_ops == op_index);

    return trace;
}

/*
 * free_trace - Free the trace record and the three arrays it points
 *              to, all of which were allocated in read_trace().
 */
void free_trace(trace_t *trace)
{
    free(trace->ops);         /* free the three arrays... */
    free(trace->blocks);
    free(trace->block_sizes);
    free(trace);              /* and the trace record itself... */
}

void run_trace_test()
{
    struct timeval time1, time2;
    int i=0, j = 0, time = 0, total_bytes = 0;
    float mem_util = 0.0f;
    int file_count = 9;
    char* filename[] = {
            "amptjp-bal.rep\0",
            "binary2-bal.rep\0",
            "binary-bal.rep\0",
            "cccp-bal.rep\0",
            "coalescing-bal.rep\0",
            "cp-decl-bal.rep\0",
            "expr-bal.rep\0",
            "random2-bal.rep\0",
            "random-bal.rep\0"};
    trace_t* tracep = 0;
    FILE* fp1 = 0;

    fp1 = fopen("test_trace","a");
    if (!fp1)
    {
        fprintf(stderr, "can not open file for logging!");
        exit (1);
    }

    for (j =0; j<file_count; ++j)
    {
        printf("using trace file: %s\n", filename[j]);

        printf("using libc, please wait...");fflush(stdout);

        tracep = read_trace("./trace_data/", filename[j]);

        gettimeofday(&time1, 0);

        total_bytes = 0;
        mem_util = 0;

        for (i = 0; i<tracep->num_ops; ++i)
        {
            if (tracep->ops[i].type == ALLOC || tracep->ops[i].type == REALLOC )
            {
                total_bytes += tracep->ops[i].size;
                tracep->block_sizes[tracep->ops[i].index] = tracep->ops[i].size;
                tracep->blocks[tracep->ops[i].index] = malloc(tracep->ops[i].size);
            }
            else if (tracep->ops[i].type == FREE)
            {
                total_bytes -= tracep->block_sizes[tracep->ops[i].index];
                free((void*)tracep->blocks[tracep->ops[i].index]);
            }
        }

        gettimeofday(&time2, 0);

        printf("done\n");

        time = abs(time2.tv_usec-time1.tv_usec);
        printf("time=%dus\t\tthroughput=%.2g\n",
                time,
                (tracep->num_ids)*1000000.0f/time);
        fprintf(fp1,"%.4g ",
                (tracep->num_ids)*1000000.0f/time);

        free_trace(tracep);

        tracep = read_trace("./trace_data/", filename[j]);

        printf("initializing our slab implementation, please wait...");fflush(stdout);

        mm_init(false);

        printf("done\n");

        printf("using our slab implementation, please wait...");fflush(stdout);

        gettimeofday(&time1, 0);

        total_bytes = 0;
        mem_util = 0;

        for (i = 0; i<tracep->num_ops; ++i)
        {
            if (tracep->ops[i].type == ALLOC || tracep->ops[i].type == REALLOC )
            {
                total_bytes += tracep->ops[i].size;
                tracep->block_sizes[tracep->ops[i].index] = tracep->ops[i].size;
                tracep->blocks[tracep->ops[i].index] = mm_malloc(tracep->ops[i].size);
            }
            else if (tracep->ops[i].type == FREE)
            {
                total_bytes -= tracep->block_sizes[tracep->ops[i].index];
                mm_free((void*)tracep->blocks[tracep->ops[i].index]);

                float cur_mem_util = total_bytes*1.0f/(page_occupied*PAGE_SIZE)*100;
                if (cur_mem_util > mem_util)
                {
                    mem_util = cur_mem_util;
                }
            }
        }

        gettimeofday(&time2, 0);

        printf("done\n");

        time = abs(time2.tv_usec-time1.tv_usec);
        printf("time=%dus\t\tthroughput=%.2g\t\tutilization=%.2f\n",
                time,
                (tracep->num_ids)*1000000.0f/time,
                mem_util);
        fprintf(fp1,"%.4g %.2f ",
                (tracep->num_ids)*1000000.0f/time,
                mem_util);

        printf("un-initializing our slab implementation, please wait...");fflush(stdout);

        mm_deinit();

        printf("done\n");

        free_trace(tracep);
    }

    fprintf(fp1,"\n");

    fclose(fp1);
}
